﻿--[[
    -------------
    Русский игровой редактор настроек транспорта для MTASA v1.2 и выше
    ---------

    Новые версии, баги, запросы:
        http://multi-theft-auto-ru.googlecode.com/

    Авторы текущего файла:
        MX_Master ( http://multi-theft-auto.ru/ )

    Легенда по префиксам в названиях переменных:
        b   boolean, булевое
        n   number, число
        s   string, строка
        t   table, таблица
        f   function, функция
        u   userdata, в MTASA этот тип юзают элементы
        th  thread, поток
]]

---
-- НАСТРОЕЧКИ --------------------------
---

-- "флаг доступа".
-- Название ElementData переменной игрока, которая дает право
-- этому самому игроку юзать этот редактор. Переменная синхрится с клиентом.
-- Эта переменная выставляется игроку, если его аккаунт находится в ACL группе,
-- указанной ниже.
-- По умолчанию: '164986523'
local sPlayerFlagForAccess = '164986523' -- не менять!

-- Закрывать ли окно редактора после применения настроек?
-- По умолчанию: false
local bCloseWindowAfterApplyActions = false

-- Кол-во чисел после точки для FLOAT чисел.
-- Используется для отображения более коротких FLOAT чисел в редакторе
-- По умолчанию: 3
local nFloatsPrecision = 3




---
-- ПРОВЕРКИ ПРАВ ДОСТУПА --
---

-- вернет true, если права для правки хэндлинга есть у локального игрока
local function fIsGUIActionsAllowed ( )
    if  not isPedInVehicle(localPlayer) or
        not getElementData( localPlayer, sPlayerFlagForAccess )
    then
        return false
    end

    return true
end




---
-- КОНВЕРТЕРЫ --
---

-- конверт числа в строку с отсечением лишних чисел после точки
local function fNumToShortStr ( n )
    if math.ceil(n) == n then
        return string.format( '%d', n )
    else
        return string.gsub( string.format( '%.'..nFloatsPrecision..'f', n ), '0+$', '' )
    end
end

-- обновляет все поля редактора согласно таблице настроек
local function fUpdateGUIFromHandlingTable ( tHandlingTable, nVehicleModel )
    -- если указана модель транспорта, то обновится заголовок
    -- с инфой о названии модели транспорта и его ИД
    if  type(nVehicleModel) == 'number' and
        nVehicleModel >= 400 and nVehicleModel <= 611
    then
        guiSetText( tGUI_Label[62], tModelToModelName[nVehicleModel] or '???' )
        guiSetText( tGUI_Label[63], tostring(nVehicleModel) )
        guiSetText( tGUI_Label[65], getVehicleNameFromModel(nVehicleModel) or '???' )
    else -- если не указана модель, то просто очистим поля заголовка
        guiSetText( tGUI_Label[62], '???' )
        guiSetText( tGUI_Label[63], '???' )
        guiSetText( tGUI_Label[65], '???' )
    end


    local t = tHandlingTable -- ссылка на таблицу

    guiSetText( tGUI_Edit[9], fNumToShortStr(t['mass']) )
    guiSetText( tGUI_Edit[13], fNumToShortStr(t['turnMass']) )
    guiSetText( tGUI_Edit[12], fNumToShortStr(t['dragCoeff']) )

    guiSetText( tGUI_Edit[11], fNumToShortStr(t['centerOfMass'][1]) )
    guiSetText( tGUI_Edit[14], fNumToShortStr(t['centerOfMass'][2]) )
    guiSetText( tGUI_Edit[15], fNumToShortStr(t['centerOfMass'][3]) )

    guiSetText( tGUI_Edit[10], fNumToShortStr(t['percentSubmerged']) )
    guiSetText( tGUI_Edit[25], fNumToShortStr(t['tractionMultiplier']) )

    local tDriveType = { ['fwd'] = 0, ['rwd'] = 1, ['awd'] = 2 }
    guiComboBoxSetSelected( tGUI_Combo[1], tDriveType[ t['driveType'] ] or 0 )

    local tEngineType = { ['petrol'] = 0, ['diesel'] = 1, ['electric'] = 2 }
    guiComboBoxSetSelected( tGUI_Combo[2], tEngineType[ t['engineType'] ] or 0 )

    guiSetText( tGUI_Edit[1],  fNumToShortStr(t['numberOfGears']) )
    guiSetText( tGUI_Edit[3],  fNumToShortStr(t['engineAcceleration']) )
    guiSetText( tGUI_Edit[2],  fNumToShortStr(t['engineInertia']) )
    guiSetText( tGUI_Edit[4],  fNumToShortStr(t['maxVelocity']) )
    guiSetText( tGUI_Edit[27], fNumToShortStr(t['brakeDeceleration']) )
    guiSetText( tGUI_Edit[26], fNumToShortStr(t['brakeBias']) )

    local tABS = { [false] = 0, [true] = 1 }
    guiComboBoxSetSelected( tGUI_Combo[19], tABS[ t['ABS'] ] or 0 )

    guiSetText( tGUI_Edit[5],  fNumToShortStr(t['steeringLock']) )
    guiSetText( tGUI_Edit[29], fNumToShortStr(t['tractionLoss']) )
    guiSetText( tGUI_Edit[28], fNumToShortStr(t['tractionBias']) )
    guiSetText( tGUI_Edit[21], fNumToShortStr(t['suspensionForceLevel']) )
    guiSetText( tGUI_Edit[20], fNumToShortStr(t['suspensionDamping']) )
    guiSetText( tGUI_Edit[19], fNumToShortStr(t['suspensionHighSpeedDamping']) )
    guiSetText( tGUI_Edit[18], fNumToShortStr(t['suspensionUpperLimit']) )
    guiSetText( tGUI_Edit[24], fNumToShortStr(t['suspensionLowerLimit']) )
    guiSetText( tGUI_Edit[22], fNumToShortStr(t['suspensionFrontRearBias']) )
    guiSetText( tGUI_Edit[23], fNumToShortStr(t['suspensionAntiDiveMultiplier']) )
    guiSetText( tGUI_Edit[6],  fNumToShortStr(t['collisionDamageMultiplier']) )
    guiSetText( tGUI_Edit[32], fNumToShortStr(t['seatOffsetDistance']) )
    guiSetText( tGUI_Edit[41], fNumToShortStr(t['monetary']) )


    -- modelFlags, handlingFlags
    local tFlagPoints = {[0]=1,[1]=2,[2]=4,[3]=8}
    local tModelFlags, tHandlingFlags = {}, {}
    local sModelFlags = string.reverse( string.format( '%08X', t['modelFlags'] ) )
    local sHandlingFlags = string.reverse( string.format( '%08X', t['handlingFlags'] ) )

    guiGridListSetSelectedItem( tGUI_Grid[1], 0, 0 )
    guiGridListSetSelectedItem( tGUI_Grid[2], 0, 0 )

    for n = 1, 8 do
        tModelFlags[n] = tonumber( '0x' .. sModelFlags:sub(n,n) )
        tHandlingFlags[n] = tonumber( '0x' .. sHandlingFlags:sub(n,n) )

        for nFlag = 3, 0, -1 do
            if tModelFlags[n] >= tFlagPoints[nFlag] and tModelFlags[n] % tFlagPoints[nFlag] >= 0 then
                guiGridListSetSelectedItem( tGUI_Grid[1], (n - 1)*4 + nFlag, 1, false )
                tModelFlags[n] = tModelFlags[n] - tFlagPoints[nFlag]
            end
            if tHandlingFlags[n] >= tFlagPoints[nFlag] and tHandlingFlags[n] % tFlagPoints[nFlag] >= 0 then
                guiGridListSetSelectedItem( tGUI_Grid[2], (n - 1)*4 + nFlag, 1, false )
                tHandlingFlags[n] = tHandlingFlags[n] - tFlagPoints[nFlag]
            end
        end
    end


    guiSetText( tGUI_Edit[17], t['headLight'] )
    guiSetText( tGUI_Edit[16], t['tailLight'] )
    guiSetText( tGUI_Edit[31], fNumToShortStr(t['animGroup']) )
end

-- возвращает таблицу настроек транспорта на основе текущих данных в окне редактора
local function fGetHandlingTableFromGUI ( )
    local t = {}

    -- edits
    t['mass'] =                         tHandlingParamLimits['mass']( guiGetText(tGUI_Edit[9]) )
    t['turnMass'] =                     tHandlingParamLimits['turnMass']( guiGetText(tGUI_Edit[13]) )
    t['dragCoeff'] =                    tHandlingParamLimits['dragCoeff']( guiGetText(tGUI_Edit[12]) )
    t['percentSubmerged'] =             tHandlingParamLimits['percentSubmerged']( guiGetText(tGUI_Edit[10]) )
    t['tractionMultiplier'] =           tHandlingParamLimits['tractionMultiplier']( guiGetText(tGUI_Edit[25]) )
    t['numberOfGears'] =                tHandlingParamLimits['numberOfGears']( guiGetText(tGUI_Edit[1]) )
    t['engineAcceleration'] =           tHandlingParamLimits['engineAcceleration']( guiGetText(tGUI_Edit[3]) )
    t['engineInertia'] =                tHandlingParamLimits['engineInertia']( guiGetText(tGUI_Edit[2]) )
    t['maxVelocity'] =                  tHandlingParamLimits['maxVelocity']( guiGetText(tGUI_Edit[4]) )
    t['brakeDeceleration'] =            tHandlingParamLimits['brakeDeceleration']( guiGetText(tGUI_Edit[27]) )
    t['brakeBias'] =                    tHandlingParamLimits['brakeBias']( guiGetText(tGUI_Edit[26]) )
    t['numberOfGears'] =                tHandlingParamLimits['numberOfGears']( guiGetText(tGUI_Edit[1]) )
    t['engineAcceleration'] =           tHandlingParamLimits['engineAcceleration']( guiGetText(tGUI_Edit[3]) )
    t['engineInertia'] =                tHandlingParamLimits['engineInertia']( guiGetText(tGUI_Edit[2]) )
    t['maxVelocity'] =                  tHandlingParamLimits['maxVelocity']( guiGetText(tGUI_Edit[4]) )
    t['brakeDeceleration'] =            tHandlingParamLimits['brakeDeceleration']( guiGetText(tGUI_Edit[27]) )
    t['brakeBias'] =                    tHandlingParamLimits['brakeBias']( guiGetText(tGUI_Edit[26]) )
    t['steeringLock'] =                 tHandlingParamLimits['steeringLock']( guiGetText(tGUI_Edit[5]) )
    t['tractionLoss'] =                 tHandlingParamLimits['tractionLoss']( guiGetText(tGUI_Edit[29]) )
    t['tractionBias'] =                 tHandlingParamLimits['tractionBias']( guiGetText(tGUI_Edit[28]) )
    t['suspensionForceLevel'] =         tHandlingParamLimits['suspensionForceLevel']( guiGetText(tGUI_Edit[21]) )
    t['suspensionDamping'] =            tHandlingParamLimits['suspensionDamping']( guiGetText(tGUI_Edit[20]) )
    t['suspensionHighSpeedDamping'] =   tHandlingParamLimits['suspensionHighSpeedDamping']( guiGetText(tGUI_Edit[19]) )
    t['suspensionUpperLimit'] =         tHandlingParamLimits['suspensionUpperLimit']( guiGetText(tGUI_Edit[18]), 50 )
    t['suspensionLowerLimit'] =         tHandlingParamLimits['suspensionLowerLimit']( guiGetText(tGUI_Edit[24]), t['suspensionUpperLimit'] )
    t['suspensionFrontRearBias'] =      tHandlingParamLimits['suspensionFrontRearBias']( guiGetText(tGUI_Edit[22]) )
    t['suspensionAntiDiveMultiplier'] = tHandlingParamLimits['suspensionAntiDiveMultiplier']( guiGetText(tGUI_Edit[23]) )
    t['collisionDamageMultiplier'] =    tHandlingParamLimits['collisionDamageMultiplier']( guiGetText(tGUI_Edit[6]) )
    t['seatOffsetDistance'] =           tHandlingParamLimits['seatOffsetDistance']( guiGetText(tGUI_Edit[32]) )
    t['monetary'] =                     tHandlingParamLimits['monetary']( guiGetText(tGUI_Edit[41]) )
    t['headLight'] =                    tHandlingParamLimits['headLight']( guiGetText(tGUI_Edit[17]) )
    t['tailLight'] =                    tHandlingParamLimits['tailLight']( guiGetText(tGUI_Edit[16]) )
    t['animGroup'] =                    tHandlingParamLimits['animGroup']( guiGetText(tGUI_Edit[31]) )
    t['centerOfMass'] =                 tHandlingParamLimits['centerOfMass'] (
        { guiGetText(tGUI_Edit[11]), guiGetText(tGUI_Edit[14]), guiGetText(tGUI_Edit[15]) }
    )


    -- comboboxes
    local tDriveType = { [-1] = 'fwd', [0] = 'fwd', [1] = 'rwd', [2] = 'awd' }
    t['driveType'] = tDriveType[ guiComboBoxGetSelected(tGUI_Combo[1]) or 0 ]

    local tEngineType = { [-1] = 'petrol', [0] = 'petrol', [1] = 'diesel', [2] = 'electric' }
    t['engineType'] = tEngineType[ guiComboBoxGetSelected(tGUI_Combo[2]) or 0 ]

    local tABS = { [-1] = false, [0] = false, [1] = true }
    t['ABS'] = tABS[ guiComboBoxGetSelected(tGUI_Combo[19]) or 0 ]


    -- modelFlags
    local nFlag, tFlagPoints = 1, {[0]=1,[1]=2,[2]=4,[3]=8}
    local tFlags = {0,0,0,0,0,0,0,0}
    local tSelected = guiGridListGetSelectedItems(tGUI_Grid[1]) or {}

    for _,tColRow in ipairs(tSelected) do
        if tColRow['column'] == 1 then
            nFlag = math.ceil( (tColRow['row'] + 1) / 4 )
            tFlags[nFlag] = tFlags[nFlag] + tFlagPoints[ (tColRow['row'] + 4) % 4 ]
        end
    end

    t['modelFlags'] = '0x'
    for n = 8, 1, -1  do
        t['modelFlags'] = t['modelFlags'] .. string.format( '%01X', tFlags[n] )
    end
    t['modelFlags'] = tonumber(t['modelFlags'])


    -- handlingFlags
    tFlags = {0,0,0,0,0,0,0,0}
    tSelected = guiGridListGetSelectedItems(tGUI_Grid[2]) or {}

    for _,tColRow in ipairs(tSelected) do
        if tColRow['column'] == 1 then
            nFlag = math.ceil( (tColRow['row'] + 1) / 4 )
            tFlags[nFlag] = tFlags[nFlag] + tFlagPoints[ (tColRow['row'] + 4) % 4 ]
        end
    end

    t['handlingFlags'] = '0x'
    for n = 8, 1, -1  do
        t['handlingFlags'] = t['handlingFlags'] .. string.format( '%01X', tFlags[n] )
    end
    t['handlingFlags'] = tonumber(t['handlingFlags'])


    return t
end




---
-- ОТКРЫТИЕ/ЗАКРЫТИЕ РЕДАКТОРА = CTRL + H --
---

-- показывает окно редактора, предварительно обновив в нем данные
function fOpenHandlingGUI ( )
    if not getKeyState("lctrl") and not getKeyState("rctrl") then return end

    -- если окно уже показано, то просто скрыть его
    if guiGetVisible(tGUI_Window[1]) then
        guiSetVisible( tGUI_Window[1], false )
        showCursor(false)
        return
    end

    if not fIsGUIActionsAllowed() then return end


    local uVehicle = getPedOccupiedVehicle(localPlayer)
    if not uVehicle then return end

    local tHandling = getVehicleHandling(uVehicle)
    if not uVehicle then return end

    fUpdateGUIFromHandlingTable( tHandling, getElementModel(uVehicle) )

    guiSetVisible( tGUI_Window[1], true )
    showCursor(true)
end
bindKey( "h", "down", fOpenHandlingGUI )




---
-- ДЕЙСТВИЯ ВНУТРИ РЕДАКТОРА --
---

-- создает обработчики для кнопок GUI окна
local function fCreateHandlingGUIHandlers ( )

    -- показывает мини окошко с коротким отчетом о каком-либо действии
    -- на время, указанное в секундах (не более 10 сек, не менее 2)
    function fShowReport ( sReport, nSeconds )
        if type(nSeconds) ~= 'number' or nSeconds <= 0 then
            nSeconds = 2
        elseif nSeconds > 10 then
            nSeconds = 10
        end

        if type(sReport) ~= 'string' then
            sReport = tostring(sReport)
        end

        if uReportTimer and isTimer(uReportTimer) then
            killTimer(uReportTimer)
            uReportTimer = false
        end

        -- скрываем
        guiSetVisible( tGUI_Window[2], false )
        guiSetText( tGUI_Label[66], sReport )

        -- центрируем относительно главного окна
        local tMainWindowPos = { guiGetPosition(tGUI_Window[1], false) }
        local tMainWindowSize = { guiGetSize(tGUI_Window[1], false) }
        local tReportWindowSize = { guiGetSize(tGUI_Window[2], false) }

        if tMainWindowSize[2] and tReportWindowSize[2] and tMainWindowPos[2] then
            guiSetPosition (
                tGUI_Window[2],
                (tMainWindowSize[1] - tReportWindowSize[1])/2 + tMainWindowPos[1],
                (tMainWindowSize[2] - tReportWindowSize[2])/2 + tMainWindowPos[2],
                false
            )
        end

        -- показываем
        guiSetVisible( tGUI_Window[2], true )
        guiBringToFront(tGUI_Window[2])

        -- ставим глобальный таймер сокрытия
        uReportTimer = setTimer( guiSetVisible, nSeconds*1000, 1, tGUI_Window[2], false )
    end


    -- изменить язык интерфейса
    function fChangeGUILanguage ( )
        local nSelected = guiComboBoxGetSelected(source)

        if type(nSelected) == 'number' and nSelected >= 0 and tLanguages[nSelected + 1] then
            fChangeLanguage( nSelected + 1 )
        end
    end
    addEventHandler( 'onClientGUIComboBoxAccepted', tGUI_Combo[20], fChangeGUILanguage, false )


    -- применить настройки к текущему транспорту
    function fApplyHandlingToVehicle ( sButton )
        if sButton ~= 'left' or not fIsGUIActionsAllowed() then return end

        local nModel = getElementModel( getPedOccupiedVehicle(localPlayer) )
        if not nModel then return end

        local tHandling = fGetHandlingTableFromGUI()

        triggerServerEvent( 'applyClientHandlingToVehicle', localPlayer,
            tHandling, getPedOccupiedVehicle(localPlayer) )

        fUpdateGUIFromHandlingTable( tHandling, nModel )

        fShowReport( fLangStr('msgApplied'), 2 )

        if bCloseWindowAfterApplyActions then
            guiSetVisible( tGUI_Window[1], false )
            showCursor(false)
        end
    end
    addEventHandler( 'onClientGUIClick', tGUI_Button[3], fApplyHandlingToVehicle, false )


    -- закрыть окно редактора
    function fCancelHandling ( sButton )
        if sButton ~= 'left' then return end

        guiSetVisible( tGUI_Window[1], false )
        showCursor(false)
    end
    addEventHandler( 'onClientGUIClick', tGUI_Button[2], fCancelHandling, false )


    -- сбросить поля редактора на стандартные настройки транспорта
    function fResetGUIToDefaultHandling ( sButton )
        if sButton ~= 'left' or not fIsGUIActionsAllowed() then return end

        local nModel = getElementModel( getPedOccupiedVehicle(localPlayer) )
        if not nModel then return end

        local tHandling = getOriginalHandling(nModel)
        if type(tHandling) ~= 'table' then return end

        fUpdateGUIFromHandlingTable( tHandling, nModel )
        fShowReport( fLangStr('msgResetedToDefaults'), 3.5 )
    end
    addEventHandler( 'onClientGUIClick', tGUI_Button[1], fResetGUIToDefaultHandling, false )


    -- функция закрывает окно редактора, если локальный игрок умер,
    -- вышел из транспорта или редактируемый транспорт взорвался
    function fHideGUIForSomeReasons ( )
        -- ничего не делать, если окно редактор итак закрыто
        if not guiGetVisible(tGUI_Window[1]) then return end

        local bHide = false
        local sSourceType = getElementType(source)

        if  sSourceType == 'vehicle' and -- мой транспорт мог взорваться
            isPedInVehicle(localPlayer) and
            getPedOccupiedVehicle(localPlayer) == source
        then
            bHide = true
        elseif sSourceType == 'player' then -- я мог выйти из транспорта или умереть
            bHide = true
        end

        if bHide then
            guiSetVisible( tGUI_Window[1], false )
            showCursor(false)
        end
    end
    addEventHandler( 'onClientPlayerVehicleExit', localPlayer, fHideGUIForSomeReasons )
    addEventHandler( 'onClientPlayerWasted', localPlayer, fHideGUIForSomeReasons )
    addEventHandler( 'onClientVehicleExplode', root, fHideGUIForSomeReasons )
end




---
-- ДЕЙСТВИЯ ПРИ СТАРТЕ РЕСУРСА --
---

addEventHandler( 'onClientResourceStart', resourceRoot,
    function()
        fCreateHandlingGUI() -- функция из файла GUI_c.lua
        fCreateHandlingGUIHandlers()
    end
)
